#include "header.h"

class H_code;
class Population;
// 匹配例子
bool isFit(string code, string example);
// 适合度函数
double FITNESS(string code);
// 交叉
void Cross(vector<H_code>& codes);

class H_code {
  friend void Cross(H_code& code1, H_code& code2);
  // 假设编码
  string code;
  // 适合度
  double fitness;
  // 轮盘赌概率
  double probability;

 public:
  // 构造函数 -- 随机生成code
  H_code(bool random = true);
  // 计算合适度
  void Fitness();
  // 变异
  void mutate();
  // 对外接口
  double get_fitness() { return fitness; }
  double get_probability() { return probability; }
  void set_probability(double val) { probability = val; }
  void operator=(H_code &h){
    code = h.code;
    fitness = 0;
    probability = 0;
  }
  string showCode(){return code;}
};

class Population {
  friend void Mutate(vector<int>& choose, Population p);
  // 假设集合
  vector<H_code> Hcodes;
  // 累计概率
  vector<double> accu_prob;
  // 最大合适度的下标
  int MaxFit;

 public:
  // 已知个数，初始化
  Population(int num = 0) : MaxFit(0) { Hcodes.resize(num); }

  // 计算合适度
  void Fitness();

  // 计算轮盘赌概率
  void Probabilities();

  // 根据轮盘赌概率选择 m 个假设
  vector<H_code> choose(int m);

  // 平均概率选择 m 个假设并进行变异
  void mutate(int m);

  // 返回合适度最高的编码
  H_code get_maxFit() { return Hcodes[MaxFit]; }

  // 添加多个编码
  void add(vector<H_code> codes) {
    for (auto it : codes) {
      Hcodes.push_back(it);
    }
  }

  void operator=(Population &p){
    Hcodes = p.Hcodes;
    accu_prob.clear();
    MaxFit = 0;
  }
};

// 匹配例子
bool isFit(string code, string example) {
  int start = 0;    // 起始位置
  bool fit = true;  // 初始布尔值
  // 对每个特征进行解码
  for (int i = 0; i < table.size(); ++i) {
    // 把该特征的代码单独拿出来
    string c_attr = code.substr(start, table[i].size());
    string e_attr = example.substr(start, table[i].size());
    start += table[i].size();
    // 每位检查，只要有一位两个都是1就行
    for (int j = 0; j < c_attr.size(); ++j) {
      // 出现一位，两个代码在这一位都是1，则break，表示这个特征匹配上了
      if (c_attr[j] == '1' && e_attr[j] == '1') break;
      // 如果到了最后一位，还没有break，那说明不匹配，需要&&false
      if (j == c_attr.size() - 1) fit = fit && false;
    }
    // 只要有一个特征没匹配上，那就整体没匹配上，直接退出循环
    if (!fit) break;
  }
  // 如果前件匹配上了，则最后一位相同表示完全匹配
  // 前件没匹配上，那最后一位不同表示完全匹配
  return fit ? code.back() == example.back() : code.back() != example.back();
}

// 适合度函数
double FITNESS(string code) {
  int fit_count = 0;
  for (auto train_code : train_codes) {
    if (isFit(code, train_code)) ++fit_count;
  }
  return fit_count / static_cast<double>(train_codes.size());
}

// 两点交叉
void Cross(H_code& code1, H_code& code2) {
  // 交叉因子
  int total = code1.code.size();                   // 总长度
  int start = rand() % total;                      // 两点交叉起点
  int end = start - 1 + rand() % (total - start);  // 两点交叉终点
  // 开始交叉
  string temp = code1.code;
  for (int i = start; i <= end; ++i) {
    code1.code[i] = code2.code[i];
    code2.code[i] = temp[i];
  }
}
void Cross(vector<H_code>& codes) {
  for (int i = 0; i < codes.size() / 2; ++i) {
    Cross(codes[i], codes[codes.size() - i - 1]);
  }
}

// 构造函数
H_code::H_code(bool random) {
  fitness = 0;
  probability = 0;
  // 不随机初始
  if (!random) {
    code = string(train_codes.front().size(), '0');
    return;
  }
  // 随机初始
  code = "";
  for (int i = 0; i < train_codes.front().size(); ++i) {
    code += rand() % 2 == 0 ? '1' : '0';
  }
}

// 计算适合度
void H_code::Fitness() { fitness = FITNESS(code); }

// 计算适合度
void Population::Fitness() {
  for (int i = 0; i < Hcodes.size(); ++i) {
    Hcodes[i].Fitness();
    MaxFit =
        Hcodes[MaxFit].get_fitness() >= Hcodes[i].get_fitness() ? MaxFit : i;
  }
}

// 计算轮盘赌概率
void Population::Probabilities() {
  double total_fit = 0;
  // 适合度总和
  for (auto it : Hcodes) {
    total_fit += it.get_fitness();
  }
  // 每个编码的概率
  for (auto& it : Hcodes) {
    it.set_probability(it.get_fitness() / total_fit);
  }
  // 计算累计概率
  for (auto it : Hcodes) {
    accu_prob.push_back(it.get_probability());
  }
  for (int i = 1; i < accu_prob.size(); ++i) {
    accu_prob[i] += accu_prob[i - 1];
  }
}

// 根据轮盘赌概率选择 m 个假设
vector<H_code> Population::choose(int m) {
  vector<H_code> res;
  for (int i = 0; i < m; ++i) {
    double point = (rand() % 100) / 100;
    for (int j = 0; j < accu_prob.size(); ++j) {
      if (accu_prob[j] > point) {
        res.push_back(Hcodes[j]);
        break;
      }
    }
  }
  return res;
}

// 代码变异
void H_code::mutate(){
  int choose = rand() % code.size();
  code[choose] = code[choose] == '1' ? '0' : '1';
}

// 平均概率选择 m 个假设
void Population::mutate(int m) {
  for (int i = 0; i < m; ++i) {
    int choose = rand() % Hcodes.size();
    Hcodes[choose].mutate();
  }
}
